package edu.isistan.genetic.representations;

import java.util.Vector;
import edu.isistan.metamodel.*;
/**
 * 
 * @author marcos.trotti
 *
 */
public class Individual implements Comparable<Individual> {
	Vector<String> operationName;
	Vector<Vector<DataType>> operationParameters;
	Vector<Vector<String>> operationParanames;
	

	Vector<DataType> returningValue;
	double fitness;
	
	public Individual(Interface aInterface){
		this();
		int i=0;
		for (Operation aOperation : aInterface.getOperations()) {
			this.operationName.add(i,aOperation.getName());
			this.operationParameters.add(i,aOperation.getParameters());
			this.operationParanames.add(i,aOperation.getParanames());
			//Should return one type as return value
			this.returningValue.add(i,aOperation.getReturnValue().elementAt(0));
			i++;
		}
	}
	
	public Individual(Individual individual){
		super();
		this.operationName = new Vector<String>(individual.operationName);
		this.operationParameters= new Vector<Vector<DataType>>();
		this.operationParanames = new Vector<Vector<String>>();
		
		for (Vector<DataType> parameters : individual.operationParameters) {
			
			Vector<DataType> cloned=new Vector<DataType>();
			for (DataType dataType : parameters) {
				cloned.add(dataType.copy());				
			}			
			this.operationParameters.add(cloned);			
		}
		
		this.operationParanames = individual.operationParanames;
		this.returningValue= new Vector<DataType>();
		for (DataType datatype : individual.returningValue) {
			this.returningValue.add(datatype.copy());
		}
		this.fitness= individual.fitness;
	}
	/**
	 * @return the operationParameters
	 */
	public Vector<Vector<DataType>> getOperationsParameters() {
		return operationParameters;
	}

	/**
	 * @param operationParameters the operationParameters to set
	 */
	public void setOperationsParameters(Vector<Vector<DataType>> operationParameters) {
		this.operationParameters = operationParameters;
	}

	public Vector<Vector<String>> getOperationParanames() {
		return operationParanames;
	}

	public void setOperationParanames(Vector<Vector<String>> operationParanames) {
		this.operationParanames = operationParanames;
	}
	
	/**
	 * @return the returningValue
	 */
	public Vector<DataType> getReturningValues() {
		return returningValue;
	}

	/**
	 * @param returningValue the returningValue to set
	 */
	public void setReturningValue(Vector<DataType> returningValue) {
		this.returningValue = returningValue;
	}

	public Individual(){
		this.operationName =  new Vector<String>();
		this.operationParameters = new Vector<Vector<DataType>>();
		this.operationParanames = new Vector<Vector<String>>();
		this.returningValue =  new Vector<DataType>();
		this.fitness = 0;
	}
	
	public Interface getInterface(){
		Interface aInterface=new Interface();
		Vector<Operation> aOperation=new Vector<Operation>();
		for (int i=0;i<this.operationName.size();i++){
			Vector<DataType> a=new Vector<DataType>();
			a.add(this.returningValue.get(i));
			aOperation.add(new Operation(this.operationName.get(i),a,this.operationParameters.get(i),this.operationParanames.get(i), "non doc"));
		}
		aInterface.setOperations(aOperation);
		return aInterface;
	}
	
	public Vector<DataType> getOperationParameters(int aIndex){
		return this.operationParameters.get(aIndex);
	}
	
	public Vector<String> getOperationParanames(int aIndex){
		return this.operationParanames.get(aIndex);
	}
	
	public DataType getOperationReturnType(int aIndex){
		return this.returningValue.get(aIndex);
	}
	
	public String getOperationName(int aIndex){
		return this.operationName.elementAt(aIndex);
	}
	
	public boolean existsOperationByName(String aOperationName){
		return this.operationName.contains(aOperationName);
	}
	
	public boolean existsOperation(String aOperationName, Vector<DataType> aOperationParams){
		return ((this.existsOperationByName(aOperationName)) && (this.operationParameters.contains(aOperationParams)));
	}
	
	public int getOperationSize(){
		return this.operationName.size();
	}
	
	public void setOperationAt(String aOperationName, DataType aReturnType, Vector<DataType> aOperationParams, Vector<String> aOperationParaNames, int aIndex){
		this.operationName.add(aIndex, aOperationName);
		this.operationParameters.add(aIndex, aOperationParams);
		this.operationParanames.add(aIndex, aOperationParaNames);
		this.returningValue.add(aIndex, aReturnType);
		return;
	}
	
	public void setParameterTo(Vector<DataType> aOperationParams,int aIndex){
		this.operationParameters.setElementAt(aOperationParams, aIndex);
	}
	
	public double getFitness(){
		return this.fitness;
	}
	
	public void setFitness(double aFitness){
		this.fitness = aFitness;
	}
	
	private String printJavaStyle(DataType parameter){
		if (parameter.isCompoused()){
			return parameter.getType() + " " + parameter.getName() ;
		}else{
			if (parameter.isArray())
				return parameter.getType() + "[] " + parameter.getName();
			else
				return parameter.getType() + " " + parameter.getName();
			}
	}
	@Override
	public String toString() {
		StringBuffer sb=new StringBuffer();
		//sb.append("Fitness: " + this.getFitness() + " \n" );
		for (int i=0;i<this.operationName.size();i++){
			if (this.returningValue.get(i).isArray())
				sb.append(this.returningValue.get(i).getType()+"[]");
			else
				sb.append(this.returningValue.get(i).getType());
			sb.append(" " + this.operationName.get(i)+ "(");
			for (int p=0; p < this.operationParameters.get(i).size() -1; p++) 
				sb.append(this.printJavaStyle(this.operationParameters.get(i).get(p))+", ");
			if (this.operationParameters.get(i).size()>=1)
				sb.append(this.printJavaStyle(this.operationParameters.get(i).get(this.operationParameters.get(i).size()-1)));
			
			sb.append(");");
			sb.append("\n");
		}
		return sb.toString();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		
		Individual other = (Individual) obj;
		if (operationName == null) {
			if (other.operationName != null)
				return false;
		} else if (!operationName.equals(other.operationName))
			return false;
		if (operationParameters == null) {
			if (other.operationParameters != null)
				return false;
		} else if (!operationParameters.equals(other.operationParameters))
			return false;
		if (operationParanames == null) {
			if (other.operationParanames != null)
				return false;
		} else if (!operationParanames.equals(other.operationParanames))
			return false;
		if (returningValue == null) {
			if (other.returningValue != null)
				return false;
		} else if (!returningValue.equals(other.returningValue))
			return false;
		return true;
	}

	/* (non-Javadoc)
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	public int compareTo(Individual arg0) {
		if (this.getFitness() > arg0.getFitness())
			return 1;
		if (this.getFitness() < arg0.getFitness())
			return -1;
		return 0;
	}
}
